#include <dinput.h>
#include "Input.h"
#include "DirectInput.h"

namespace helix
{

	DirectInput::DirectInput() : Input()
	{
		available=false;									//flag to let us know if directinput is available or not
		keyboardAvailable=false;							//flag to let us know if the keyboard has been initialized
		mouseAvailable=false;								//flag to let us know if the mouse has been initialized
		mouseBufferSize=16;									//bytes for a mouse buffer
	}

	DirectInput::DirectInput(int bufferSize) : Input(bufferSize)
	{
		available=false;									//flag to let us know if directinput is available or not
		keyboardAvailable=false;							//flag to let us know if the keyboard has been initialized
		mouseAvailable=false;								//flag to let us know if the mouse has been initialized
		mouseBufferSize=16;									//bytes for a mouse buffer
	}

	DirectInput::DirectInput(HINSTANCE g_hinst) : Input()
	{
		available=false;									//flag to let us know if directinput is available or not
		keyboardAvailable=false;							//flag to let us know if the keyboard has been initialized
		mouseAvailable=false;								//flag to let us know if the mouse has been initialized
		mouseBufferSize=16;									//bytes for a mouse buffer
	}

	DirectInput::~DirectInput()                                   //deinitialize Direct Input
	{
		DI_Term();
	}

	void DirectInput::initialize(HINSTANCE g_inst,HWND g_hwndMain)
	{
		hr = DirectInput8Create(g_inst, DIRECTINPUT_VERSION,   //try to initialize Direct Input
		IID_IDirectInput8, (void**)&g_lpDI, NULL); 

		if (FAILED(hr)) 
		{ 
			logFile.Error("DirectInput8 Not available.");			// DirectInput not available
		} 
		else 
		{
			logFile.Println("Direct Input Available.");
			available=true;
		}

		if(!InitKeyboardDevice(g_hwndMain))
			logFile.Error("Keyboard input not available.");

		if(!InitMouseDevice(g_hwndMain))
			logFile.Error("Mouse input not available.");
	}

	bool DirectInput::InitKeyboardDevice(HWND g_hwndMain)					//create a keyboard device
	{
		// create DInput device
		logFile.Println("Attempting to initialize Keyboard Device...");
		hr = g_lpDI->CreateDevice(GUID_SysKeyboard, &g_lpKeyboardDevice, NULL); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Failed to create directinput device (keyboard).");
			DI_Term();
			return false;
		}
		logFile.Println("   Keyboard device (DirectInput) created.");

		// Set device data format 
		hr = g_lpKeyboardDevice->SetDataFormat(&c_dfDIKeyboard); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Could not set keyboard data format.");
			DI_Term(); 
			return false; 
		} 
		logFile.Println("   Keyboard Data Format Set.");

		// Set device cooperation level
		hr = g_lpKeyboardDevice->SetCooperativeLevel(g_hwndMain, 
					DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Could not set keyboard cooperation level.");
			DI_Term(); 
			return false; 
		}
		logFile.Println("   Keyboard cooperation level set.");

		// Acquire device
		if (g_lpKeyboardDevice) 
		{
			g_lpKeyboardDevice->Acquire(); 
			logFile.Println("   Keyboard device acquired without errors. \n...Keyboard Initialized.");
			keyboardAvailable=true;
			return true;
		}
		
		logFile.Error("Keyboard device not acquired.");
		return false;
	}

	bool DirectInput::InitMouseDevice(HWND g_hwndMain)							//create a mouse device
	{
		//create DInput device
		logFile.Println("Attempting to Initialize Mouse Device...");
		hr = g_lpDI->CreateDevice(GUID_SysMouse, &g_lpMouseDevice, NULL); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Failed to create directinput device (mouse).");
			DI_Term();
			return false;
		}
		logFile.Println("   Mouse device (DirectInput) created.");

		// Set device data format
		hr = g_lpMouseDevice->SetDataFormat(&c_dfDIMouse); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Could not set mouse data format.");
			DI_Term(); 
			return false; 
		} 
		logFile.Println("   Mouse Data Format Set.");

		// Set device cooperation level
		hr = g_lpMouseDevice->SetCooperativeLevel(g_hwndMain, 
					DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 
		if (FAILED(hr)) 
		{ 
			logFile.Error("Could not set mouse cooperation level.");
			DI_Term(); 
			return false; 
		}
		logFile.Println("   Mouse cooperation level set.");

		//create a handle to a mouse event
	/*	HANDLE g_hMouseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 		if (g_hMouseEvent == NULL) 
		{
			logFile.Error("Mouse Event Handle not created.");
			return false;
		}
		logFile.Println("   Mouse Event Handle Created.");
	 
		//add the event to the mouse device
		hr = g_lpMouseDevice->SetEventNotification(g_hMouseEvent);
		if (FAILED(hr)) 
		{
			logFile.Error("Error adding Event Handle to Mouse Device.");
			return false;
		}
		logFile.Print;n("   Event Handle Added to Mouse Device.");

		// create a dinput property for the mouse
		DIPROPDWORD dipdw;
		// the header
		dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
		dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
		dipdw.diph.dwObj        = 0;
		dipdw.diph.dwHow        = DIPH_DEVICE;
		// the data
		dipdw.dwData            = mouseBufferSize;


		hr = g_lpMouseDevice->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
		if (FAILED(hr)) 
		{
			logFile.Error("Error setting mouse buffer property.");
			return false;
		}
		logFile.Println("   Mouse Buffer Property Set: %d Bytes.",mouseBufferSize);
	*/

		// acquire the device
		if (g_lpMouseDevice) 
		{
			g_lpMouseDevice->Acquire(); 
			logFile.Println("   Mouse device acquired without errors. \n...Mouse Initialized.");
			mouseAvailable=true;
			return true;
		}
		
		logFile.Error("Mouse device not acquired.");
		return false;
	}

	bool DirectInput::UpdateKeyboardState()
	{
		if (!keyboardAvailable)
		{
			return false;
		}



		hr = g_lpKeyboardDevice->GetDeviceState(sizeof(buffer),(LPVOID)buffer);
		if (FAILED(hr))										//if we can't get the device state , find out what went wrong
		{
			if (hr==DIERR_INPUTLOST || hr==DIERR_NOTACQUIRED)						//the input was lost
			{
				hr= g_lpKeyboardDevice->Acquire();			//try to reacquire it
				if(FAILED(hr))
				{
					return false;
				}
			}
			//after reacquiring, try toget the device state again
			hr = g_lpKeyboardDevice->GetDeviceState(sizeof(buffer),(LPVOID)buffer);
			if (FAILED(hr))
			{
				return false;
			}
			return true;
		}
		return true;
	}

	bool DirectInput::UpdateMouseState()
	{
		if (!mouseAvailable)
		{
			return false;
		}

		hr = g_lpMouseDevice->GetDeviceState(sizeof(mouseState),(LPVOID)&mouseState);
		if (FAILED(hr))										//if we can't get the device state , find out what went wrong
		{
			if (hr==DIERR_INPUTLOST || hr==DIERR_NOTACQUIRED)						//the input was lost
			{
				hr= g_lpMouseDevice->Acquire();			//try to reacquire it
				if(FAILED(hr))
				{
					return false;
				}
			}
			//after reacquiring, try to get the device state again
			hr = g_lpMouseDevice->GetDeviceState(sizeof(mouseState),(LPVOID)&mouseState);
			if (FAILED(hr))
			{
				return false;
			}
			return true;
		}
		return true;
	}

	bool DirectInput::UpdateDeviceStates()
	{
		bool passed=false;

		if(UpdateKeyboardState() && UpdateMouseState())
			passed=true;

		return passed;
	}

	void DirectInput::update()
	{
		UpdateDeviceStates();
	}


	//call UpdateKeyboardState() or UpeateDeviceStates() to recieve the current keyboard state
	bool DirectInput::keyDown(unsigned char keyDown)
	{
		if KEYDOWN(buffer,keyDown)
			return true;

		return false;
	}

	bool DirectInput::mouseDown(unsigned char mouseButton)
	{
		unsigned char pressed = mouseState.rgbButtons[0] & H_LEFT_MOUSE_BUTTON 
							| mouseState.rgbButtons[1] & H_RIGHT_MOUSE_BUTTON
							| mouseState.rgbButtons[2] & H_MIDDLE_MOUSE_BUTTON;

		if(pressed == mouseButton)
			return true;

		return false;
	}

	bool DirectInput::leftMouse()							//return true if the left mouse button is being pressed
	{
		if (mouseState.rgbButtons[0])
			return true;

		return false;
	}

	bool DirectInput::rightMouse()							//return true if the left mouse button is being pressed
	{
		if (mouseState.rgbButtons[1])
			return true;

		return false;
	}

	bool DirectInput::middleMouse()							//return true if the left mouse button is being pressed
	{
		if (mouseState.rgbButtons[2])
			return true;

		return false;
	}

	long DirectInput::getMouseX()								//return the mouse position in the x direction
	{
		return (long)mouseState.lX;
	}

	long DirectInput::getMouseY()								//return the mouse position in the y direction
	{
		return (long)mouseState.lY;
	}

	long DirectInput::getMouseZ()								//return the position of the scroll
	{
		return (long)mouseState.lZ;
	}

	void DirectInput::DI_Term() 
	{ 
		if (g_lpDI) 
		{
			logFile.Println("Attempting to shut down Direct Input object...");
			if (keyboardAvailable) 
			{ 
			// Always unacquire device before calling Release(). 
				g_lpKeyboardDevice->Unacquire(); 
				g_lpKeyboardDevice->Release();
				g_lpKeyboardDevice = NULL; 
				logFile.Println("   Keyboard Device shut down.");
			}
			if (mouseAvailable)
			{
				g_lpMouseDevice->Unacquire();
				g_lpMouseDevice->Release();
				g_lpMouseDevice = NULL;
				logFile.Println("   Mouse Device shut down.");
			}
			g_lpDI->Release();
			g_lpDI = NULL; 
			logFile.Println("...Direct Input Object shut down");
		}
		available=false;
		keyboardAvailable=false;
		mouseAvailable=false;
	} 


}